package com.gitee.wsl.text

import com.gitee.wsl.ext.base.Arrays.arraycopy
import com.gitee.wsl.ext.base.Arrays.copyOf
import com.gitee.wsl.ext.string.EMPTY
import com.gitee.wsl.ext.string.charAt
import kotlin.jvm.JvmOverloads
import kotlin.math.min


/**
 * 相比于StringBuilder提供更多帮助函数
 */
class MutableString(buf: CharArray, start: Int, limit: Int) : CharSequence, Appendable {
    //  static final char[] EMPTY_CHARS = new char[0];
    private var buf: CharArray
    private var start: Int
    private var limit: Int

    init {
        require(start >= 0){"buf start"}
        require(limit >= start, {"buf limit"})
        this.buf = buf
        this.start = start
        this.limit = limit
    }

    fun accept(c: Int) {
        append(c.toChar())
    }

    @JvmOverloads
    constructor(bufSize: Int = 32) : this(CharArray(bufSize), 0, 0)

    constructor(str: String) : this(str.toCharArray(), 0, str.length)

    fun clear() {
        this.start = 0
        this.limit = 0
    }

    fun resetLimit() {
        this.limit = start
    }

    fun start(start: Int): MutableString {
        checkPositionIndex(start, buf.size)

//        if (start > limit) throw NopException(ERR_TEXT_BUF_START_EXCEED_LIMIT).param(ARG_START, start)
//            .param(ARG_LIMIT, limit)
        require(start <= limit)
        this.start = start
        return this
    }

    fun limit(limit: Int): MutableString {
//        if (limit < start)
//            throw NopException(ERR_TEXT_BUF_START_EXCEED_LIMIT).param(ARG_START, start).param(ARG_LIMIT, limit)
//        if (limit > buf.size)
//            throw NopException(ERR_TEXT_BUF_LIMIT_EXCEED_LENGTH).param(ARG_LIMIT, limit).param(ARG_LENGTH, capacity())
        require(limit >= start)
        require(limit <= buf.size)
        this.limit = limit
        return this
    }

    fun skipLeading(n: Int): MutableString {
        start = min(start + n, limit)
        return this
    }

    fun skipTrailing(n: Int): MutableString {
        limit = min(start, limit - n)
        return this
    }

    fun start(): Int {
        return start
    }

    fun limit(): Int {
        return limit
    }

    fun capacity(): Int {
        return buf.size
    }

    fun free(): Int {
        return buf.size - limit
    }

    override val length: Int
        get() = limit - start


    override fun get(index: Int): Char {
        checkPositionIndex(index, length)
        return buf[start + index]
    }

    override fun toString(): String {
        if (this.isEmpty()) return String.EMPTY
        return buf.concatToString(start, start + (limit - start))
    }

    override fun subSequence(start: Int, end: Int): MutableString {
        checkPositionIndex(start, length)
        return MutableString(buf, this.start + start, min(this.start + end, this.limit))
    }

    private fun checkPositionIndex(start: Int, length: Int) {
        require(start in 0..length)
    }

    fun subSequence(start: Int): MutableString {
        return subSequence(start, length)
    }

    @JvmOverloads
    fun substring(start: Int, end: Int = length): String {
        if (start == end) return String.EMPTY

        checkPositionIndex(start, length)
        val offset = this.start + start
        return buf.concatToString(offset, offset + min(start + end, this.limit))
    }

    fun upperCase(): MutableString {
        for (i in start..<limit) {
            this.buf[i] = this.buf[i].uppercaseChar()
        }
        return this
    }


    /**
     * 从指定位置开始向后查找字符
     *
     * @param c
     * @param pos
     * @return
     */
    /**
     * 从当前位置开始向后查找字符
     *
     * @param c
     * @return
     */
    @JvmOverloads
    fun indexOf(c: Char, pos: Int = 0): Int {
        for (i in start + pos..<limit) {
            val cc = buf[i]
            if (cc == c) return i - start
        }
        return -1
    }

    /**
     * 从指定位置开始向前查找字符
     *
     * @param c
     * @param pos
     * @return
     */
    /**
     * 从后向前查找字符
     *
     * @param c
     * @return
     */
    @JvmOverloads
    fun lastIndexOf(c: Char, pos: Int = length - 1): Int {
        for (i in start + pos downTo start) {
            val cc = buf[i]
            if (cc == c) return i - start
        }
        return -1
    }

    @JvmOverloads
    fun indexOf(str: String, pos: Int = 0): Int {
        var pos = pos
        //checkPositionIndex(pos, length)

        pos += start
        if (pos + str.length > limit) return -1

        var i = pos
        val n = limit - str.length
        while (i < n) {
            if (_startsWith(str, i)) return i - start
            i++
        }
        return -1
    }

    fun trim(): MutableString {
        return trimLeading().trimTrailing()
    }

    fun trimLeading(): MutableString {
        var begin = start
        val end = limit

        while (begin < end) {
            val c = buf[begin]
            if (!c.isWhitespace()) {
                break
            }
            begin++
        }
        start = begin
        return this
    }

    fun trimTrailing(): MutableString {
        val begin = start
        var end = limit

        while (end > begin) {
            val c = buf[end - 1]
            if (!c.isWhitespace()) {
                break
            }
            end--
        }
        limit = end

        return this
    }

    fun trimLeading(c: Char): MutableString {
        var begin = start
        val end = limit

        while (begin < end) {
            if (buf[begin] != c) break
            begin++
        }
        start = begin
        return this
    }

    fun trimTrailing(c: Char): MutableString {
        val begin = start
        var end = limit

        while (end > begin) {
            if (buf[end - 1] != c) break
            end--
        }
        limit = end

        return this
    }

    @JvmOverloads
    fun indexOfAny(chars: CharArray, pos: Int = 0): Int {
        for (i in start + pos..<limit) {
            val c = buf[i]
            for (cc in chars) {
                if (cc == c) {
                    return i - start
                }
            }
        }
        return -1
    }

    /**
     * 查找在一个区间内的任意一个字符
     *
     * @param first
     * @param last
     * @return
     */
    @JvmOverloads
    fun indexOfRange(first: Char, last: Char, pos: Int = 0): Int {
        for (i in start + pos..<limit) {
            val c = buf[i]
            if (c in first..last) {
                return i - start
            }
        }
        return -1
    }

    @JvmOverloads
    fun startWith(c: Char, pos: Int = 0): Boolean {
        if (pos >= length) return false

        return buf[start + pos] == c
    }

    fun endWith(c: Char): Boolean {
        return startWith(c, length - 1)
    }

    fun _matchAny(chars: CharArray, pos: Int): Boolean {
        val c = buf[pos]
        for (cc in chars) {
            if (cc == c) return true
        }
        return false
    }

    @JvmOverloads
    fun startWithAny(chars: CharArray, pos: Int = 0): Boolean {
        if (pos >= length) return false
        return _matchAny(chars, start + pos)
    }

    fun endWithAny(chars: CharArray): Boolean {
        return startWithAny(chars, length - 1)
    }

    @JvmOverloads
    fun startsWith(str: CharSequence, pos: Int = 0): Boolean {
        if (pos < 0) return false

        if (pos + str.length > length) return false
        return _startsWith(str, start + pos)
    }

    fun _startsWith(str: CharSequence, pos: Int): Boolean {
        var i = 0
        val n = str.length
        while (i < n) {
            val c1 = buf[pos + i]
            val c2 = str.get(i)
            if (c1 != c2) return false
            i++
        }
        return true
    }

    fun endsWith(str: CharSequence): Boolean {
        return startsWith(str, length - str.length)
    }

    fun match(str: CharSequence): Boolean {
        return length == str.length && startsWith(str)
    }

    @JvmOverloads
    fun startsWithIgnoreCase(other: CharSequence, pos: Int = 0): Boolean {
        return regionMatches(true, pos, other, 0, other.length)
    }

    fun regionMatches(ignoreCase: Boolean, toffset: Int, other: CharSequence, ooffset: Int, len: Int): Boolean {
        var len = len
        if ((ooffset < 0) || (toffset < 0) || (toffset > limit - len) || (ooffset > other.length - len)) {
            return false
        }

        val ta: CharArray? = buf
        var to = toffset + start
        var po = ooffset

        while (len-- > 0) {
            val c1 = ta!![to++]
            val c2 = other.get(po++)
            if (c1 != c2) {
                if (ignoreCase) {
                    val u1 = c1.uppercaseChar()
                    val u2 = c2.uppercaseChar()
                    if (u1 == u2) {
                        continue
                    }
                    if (u1.lowercaseChar() == u2.lowercaseChar()) {
                        continue
                    }
                }
                return false
            }
        }
        return true
    }

    fun getChars(srcBegin: Int, srcEnd: Int, dst: CharArray, dstBegin: Int) {
        if (srcBegin < 0) {
            throw IndexOutOfBoundsException("srcBegin:$srcBegin")
        }
        if (srcEnd > length) {
            throw IndexOutOfBoundsException("srcEnd:$srcBegin")
        }
        if (srcBegin > srcEnd) {
            throw IndexOutOfBoundsException("offset:${srcEnd - srcBegin}")
        }
        arraycopy(buf, start + srcBegin, dst, dstBegin, srcEnd - srcBegin)
    }



    override fun append(value: CharSequence?): MutableString {
        return append(value, 0, value?.length?:0)
    }

    override fun append(value: CharSequence?, startIndex: Int, endIndex: Int): MutableString {
        var str = value
        if (str == null) str = "null"

        require(endIndex >= startIndex){ "invalid range:$startIndex - $endIndex"}

        val len = endIndex - startIndex
        ensureCapacity(limit + len)
        if (str is String) {
            str.toCharArray(buf, limit, startIndex, endIndex)
        } else {
            var i = startIndex
            var j = limit
            while (i < endIndex) {
                buf[j] = str.get(i)
                i++
                j++
            }
        }
        limit += len
        return this
    }

    private fun ensureCapacity(minimumCapacity: Int) {
        if (minimumCapacity - buf.size > 0) {
            buf = copyOf(buf, newCapacity(minimumCapacity))
        }
    }

    private fun newCapacity(minCapacity: Int): Int {
        var newCapacity = (buf.size shl 1) + 2
        if (newCapacity > minCapacity + 1024 * 1024) {
            newCapacity = minCapacity + 1024 * 1024
        } else if (newCapacity < minCapacity) {
            newCapacity = minCapacity + 16
        }
        return newCapacity
    }

    override fun append(value: Char): MutableString {
        ensureCapacity(limit + 1)
        buf[limit++] = value
        return this
    }

//    fun escapeJava(c: Char): MutableString {
//        try {
//            StringHelper.escapeCharTo(c, StringHelper.JAVA_ESCAPE_CHARS, StringHelper.JAVA_ESCAPE_STRS, this)
//        } catch (e: IOException) {
//            throw NopException.adapt(e)
//        }
//        return this
//    }
//
//    fun escapeJava(str: String?): MutableString {
//        try {
//            StringHelper.escapeTo(str, StringHelper.JAVA_ESCAPE_CHARS, StringHelper.JAVA_ESCAPE_STRS, this)
//        } catch (e: IOException) {
//            throw NopException.adapt(e)
//        }
//        return this
//    }
//
//    fun escapeXml(c: Char): MutableString {
//        try {
//            StringHelper.escapeCharTo(c, StringHelper.XML_ESCAPE_CHARS, StringHelper.XML_ESCAPE_STRS, this)
//        } catch (e: IOException) {
//            throw NopException.adapt(e)
//        }
//        return this
//    }
//
//    fun escapeXml(str: String?): MutableString {
//        try {
//            StringHelper.escapeTo(str, StringHelper.XML_ESCAPE_CHARS, StringHelper.JAVA_ESCAPE_STRS, this)
//        } catch (e: IOException) {
//            throw NopException.adapt(e)
//        }
//        return this
//    }

    fun replace(oldChar: Char, newChar: Char): MutableString {
        for (i in start..<limit) {
            if (buf[i] == oldChar) buf[i] = newChar
        }
        return this
    }

    fun replace(begin: Int, end: Int, str: CharSequence): MutableString {
        var end = end
        if (end > limit) end = limit
        val len = str.length
        val newCount = limit + len - (end - begin)

        ensureCapacity(newCount)

        arraycopy(buf, end, buf, begin + len, limit - end)
        if (str is String) {
            str.toCharArray(buf, begin, 0, len)
        } else {
            for (i in 0..<len) {
                buf[begin + i] = str.get(i)
            }
        }
        limit = newCount
        return this
    }

    fun insert(offset: Int, str: CharSequence?): MutableString {
        var offset = offset
        var str = str
        if ((offset < 0) || (offset > length)) throw IndexOutOfBoundsException("offset:$offset")
        if (str == null) str = "null"
        val len = str.length
        ensureCapacity(limit + len)
        offset = start + limit
        arraycopy(buf, offset, buf, offset + len, limit - offset)
        if (str is String) {
            str.toCharArray(buf, offset, 0, len)
        } else {
            for (i in 0..<len) {
                buf[offset + i] = str.get(i)
            }
        }
        limit += len
        return this
    }

    fun deleteCharAt(pos: Int): MutableString {
        return delete(pos, pos + 1)
    }

    fun delete(begin: Int, end: Int): MutableString {
        var end = end
        if (end > limit) end = limit
        val newCount = limit - (end - begin)

        arraycopy(buf, end, buf, begin, limit - end)
        limit = newCount
        return this
    }

    fun replace(subStr: String, repStr: String): MutableString {
        var pos = 0
        do {
            val i = this.indexOf(subStr, pos)
            if (i < 0) return this

            replace(i, i + subStr.length, repStr)
            pos = i + repStr.length
        } while (true)
    }

    fun deleteWhitespace(): MutableString {
        var i = start
        var n = limit()
        while (i < n) {
            val c = get(i)
            if (c.isWhitespace()) {
                deleteCharAt(i)
                i--
                n--
            }
            i++
        }
        return this
    }
}